package org.seefin.formats.iso8583.types;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Representation of an ISO8583 Message Type Indicator
 * 
 * @author phillipsr
 *
 */
public final class MTI
	implements Comparable<MTI>
{
	// regex defining valid MTI values
	private static final String ValidMTI = "[0129][123456789][0123489][012345]";

	private static final Matcher ValidMTIMatcher 
		= Pattern.compile ( ValidMTI).matcher("");

	private final String value;

	/**
	 * 
	 * @param code
	 * @throws IllegalArgumentException if the code is null, not 4 chars in length,
	 * 			or does not conform to the valid MTI pattern: <code>[0129][123456789][0123489][012345]</code>
	 */
	private MTI ( char[] code)
	{
		if ( code == null || code.length != 4)
		{
			throw new IllegalArgumentException ( 
					"MTI must not be null and four chars long [" + Arrays.toString ( code) + "]");
		}
		value = new String(code).trim();
		if ( value.length() != 4)
		{
			throw new IllegalArgumentException ( "MTI must be exactly 4 digits in length, got [" + value + "]");
		}
		ValidMTIMatcher.reset ( value);
		if ( ValidMTIMatcher.matches() == false)
		{
			throw new IllegalArgumentException ( "MTI must be numeric and conform to pattern: [" + ValidMTI 
					+ "]: got [" + value + "]");
		}
	}
	
	/**
	 * Create an MTI object from its string representation
	 * @param code
	 * @return
	 */
	public static MTI
	create ( String code)
	{
		return new MTI ( code.toCharArray ());
	}
	
	/**
	 * Create an MTI object from its numeric representation
	 * @param code
	 * @return
	 */
	public static MTI
	create ( int code)
	{
		char[] mti = new char[] { 
				(char)( ((code & 0xf000) >> 12) + 0x30),
				(char)( ((code & 0x0f00) >> 8) + 0x30),
				(char)( ((code & 0x00f0) >> 4) + 0x30),
				(char)( ((code & 0x000f) ) + 0x30)};
		return new MTI ( mti);
	}
	
	private static Map<Character,String> versions 
		= new HashMap<Character,String>()
			{{
				put ( '0', "ISO 8583-1:1987");
				put ( '1', "ISO 8583-1:1993");
				put ( '2', "ISO 8583-1:2003");
				put ( '9', "Private");
			}};
	
	private static Map<Character,String> messageClasses 
		= new HashMap<Character,String>()
			{{
				put ( '1', "Authorization Message");
				put ( '2', "Financial Message");
				put ( '3', "File Actions Message");
				put ( '4', "Reversal Message");
				put ( '5', "Reconciliation Message");
				put ( '6', "Administrative Message");
				put ( '7', "Fee Collection Messages");	
				put ( '8', "Network Management Message");
				put ( '9', "Reserved by ISO");
			}};
			
	private static Map<Character,String> messageFunctions 
		= new HashMap<Character,String>()
			{{
				put ( '0', "Request");
				put ( '1', "Request Response");
				put ( '2', "Advice");
				put ( '3', "Advice Response");
				put ( '4', "Notification");
				put ( '8', "Response acknowledgment");
				put ( '9', "Negative acknowledgment");
			}};
			
	private static Map<Character,String> messageOrigins 
		= new HashMap<Character,String>()
			{{
				put ( '0', "Acquirer");
				put ( '1', "Acquirer Repeat");
				put ( '2', "Issuer");
				put ( '3', "Issuer Repeat");
				put ( '4', "Other");
				put ( '5', "Other Repeat");				
			}};
		
	private static final int VERSION_POS = 0;
	private static final int CLASS_POS = 1;
	private static final int FUNCTION_POS = 2;
	private static final int ORIGIN_POS = 3;
	
	public String
	getVersion()
	{
		return versions.get ( value.charAt(VERSION_POS));
	}
	
	public String
	getMessageClass()
	{
		return messageClasses.get ( value.charAt(CLASS_POS));
	}
	
	public String
	getMessageFunction()
	{
		return messageFunctions.get ( value.charAt(FUNCTION_POS));
	}	
	
	public String
	getMessageOrigin()
	{
		return messageOrigins.get ( value.charAt(ORIGIN_POS));
	}
	
	public String
	describe()
	{
		return value + " (version=" + getVersion ()
				+ " class=" + getMessageClass ()
				+ " function=" + getMessageFunction ()
				+ " origin=" + getMessageOrigin () + ")";
	}
	
	
	@Override
	public String
	toString()
	{
		return value;
	}
	
	@Override
	public boolean
	equals(Object other)
	{
		if ( this == other)
		{
			return true;
		}
		if ( other == null || other.getClass() != this.getClass())
		{
			return false;
		}
		return value.equals ( ((MTI)other).value);
	}

	@Override
	public int
	hashCode()
	{
		return value.hashCode();
	}
	
	public int
	intValue()
	{
		return Integer.parseInt ( value, 16);
	}
	
	@Override
	public int
	compareTo ( MTI other)
	{
		return value.compareTo ( other.value);
	}
	
}
